/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  ZippedObjectMap.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2020/06/04
 *Desc		:  //用于主要说明此程序文件完成的主要功能
 *Relation :
 *Others	:  //其他内容说明
 *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
 *History	:
 * //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
 ***************************************************************************************************/
#include "ZippedObjectMap.h"

#include <core/Utility.h>

#include <string>

#include "BondInfo.h"
#include "CompanyReport.h"

#ifndef BASSERT
#define BASSERT Q_ASSERT
#endif  // !BASSERT

#ifndef _MSC_VER
#define RTL_FIELD_SIZE(type, field) (sizeof(((type*)0)->field))
#define FIELD_OFFSET(type, field) offsetof(type, field)
#endif

// static_assert(sizeof(std::string) == 24, "");
ReportEncoder::FieldArray* ReportEncoder::FieldArray::Create(
    const char* src, const FieldOffset& fields) {
  const std::vector<FieldInfo>& offsets = fields.m_fields;
  int headBytes =
      sizeof(ushort) + sizeof(ushort) + sizeof(Offset) * offsets.size();
  int bytes = headBytes;
  // vector<int>	sizes; sizes.reserve(offsets.size());
  qb::base::StackArray<int, 128> sizes;
  const int typeSize[kTypeCount] = {sizeof(char),   sizeof(time_t),
                                    sizeof(int),    sizeof(int64_t),
                                    sizeof(double), 0};
  for (int i = 0; i < (int)offsets.size(); i++) {
    const FieldInfo& info = offsets[i];
    int encodeSize = typeSize[info.type];
    if (info.type == kTypeString) encodeSize = strlen(src + info.pos) + 1;
    if (IsDefault(src + info.pos, encodeSize)) encodeSize = 0;
    bytes += encodeSize;
    sizes.Add(encodeSize);
  }

  char* buf = qb::base::EncodeBuf::Alloc(bytes);
  FieldArray* farray = (FieldArray*)buf;
  farray->m_count = (ReportEncoder::ushort)offsets.size();
  farray->m_bytes = bytes;
  Offset* offs = farray->m_offsets;
  ushort pos = headBytes;
  for (int i = 0; i < farray->m_count; i++) {
    const FieldInfo& info = offsets[i];
    offs[i] = Offset(info.type, sizes[i] == 0 ? 1 : 0, pos);
    if (sizes[i] > 0) memcpy(buf + pos, src + info.pos, sizes[i]);
    pos += sizes[i];
  }
  BASSERT(pos == bytes);
  return farray;
}

void ReportEncoder::FieldArray::Destroy(FieldArray* farray) {
  if (!farray) return;
  qb::base::EncodeBuf::Free((char*)farray);
}

bool ReportEncoder::FieldArray::Decode(const FieldArray* farray, char* dest,
                                       const FieldOffset& fields) {
  const std::vector<FieldInfo>& offsets = fields.m_fields;
  if (farray->m_count != offsets.size()) return false;

  memset(dest, 0, sizeof(REPORT));
  const char* src = (const char*)farray;
  const int typeSize[kTypeCount] = {sizeof(char),   sizeof(time_t),
                                    sizeof(int),    sizeof(int64_t),
                                    sizeof(double), 0};

  for (int i = 0; i < (int)offsets.size(); i++) {
    const Offset& off = farray->m_offsets[i];
    const FieldInfo& info = offsets[i];
    if (off.dft == 1) continue;
    // if (off.type == kTypeString)
    //	FIELDCOPYN(dest + info.pos, info.len, src + off.pos);
    // else
    //	memcpy(dest + info.pos, src + off.pos, typeSize[off.type]);
    switch (off.type) {
      case kTypeChar:
        dest[info.pos] = src[off.pos];
        break;
      case kTypeTime:
        memcpy(dest + info.pos, src + off.pos, sizeof(time_t));
        break;
      case kTypeInt:
        memcpy(dest + info.pos, src + off.pos, sizeof(int));
        break;
      case kTypeInt64:
        memcpy(dest + info.pos, src + off.pos, sizeof(int64_t));
        break;
      case kTypeDouble:
        memcpy(dest + info.pos, src + off.pos, sizeof(double));
        break;
      case kTypeString:
        FIELDCOPYN(dest + info.pos, info.len, src + off.pos);
        break;
    }
  }
  return true;
}

void ReportEncoder::MetaInit::InitReport(FieldOffset* meta) {
#define ADDFIELD(TYPE, OBJ, FIELD)                          \
  index = meta->Add(#FIELD, TYPE, FIELD_OFFSET(OBJ, FIELD), \
                    RTL_FIELD_SIZE(OBJ, FIELD))
#define ADDFIELD_EX(TYPE, OBJ, FIELD, POS)                        \
  index = meta->Add(#FIELD, TYPE, POS + FIELD_OFFSET(OBJ, FIELD), \
                    RTL_FIELD_SIZE(OBJ, FIELD))

  int index = 0;
  ADDFIELD(kTypeInt, REPORT, m_dwVersion);
  ADDFIELD(kTypeInt, REPORT, m_dwMarket);
  ADDFIELD(kTypeString, REPORT, m_sBondKey);
  ADDFIELD(kTypeString, REPORT, m_sListedMarket);
  ADDFIELD(kTypeTime, REPORT, m_time);
  ADDFIELD(kTypeTime, REPORT, m_create_time);
  ADDFIELD(kTypeString, REPORT, m_bp_status);
  ADDFIELD(kTypeString, REPORT, m_company_id);
  ADDFIELD(kTypeString, REPORT, m_price_status);

  BASSERT(index == (int)kReportField::PriceStatus);

  // 两个PRICE结构体
  int pos = FIELD_OFFSET(REPORT, m_bidinfo);
  for (int i = 0; i < 2; i++) {
    ADDFIELD_EX(kTypeString, PRICE, m_id, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_bp_status, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_flag_bargain, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_flag_relation, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_flag_vip, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_company_id, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_flag_bad, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_price_status, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_rebate, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_return_point, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_clean_price, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_full_price, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_yield_price, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_sPrice, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_quotetype, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_sVolume, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_description, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_interpret, pos);
    ADDFIELD_EX(kTypeString, PRICE, m_exercise, pos);
    ADDFIELD_EX(kTypeTime, PRICE, m_create_time, pos);
    ADDFIELD_EX(kTypeTime, PRICE, m_time, pos);
    pos = FIELD_OFFSET(REPORT, m_askinfo);
    BASSERT((i == 0 && index == (int)kReportField::Bid_time) ||
            (i == 1 && index == (int)kReportField::Ask_time));
  }

  BASSERT(index == (int)kReportField::Ask_time);
  // m_indx_info
  pos = FIELD_OFFSET(REPORT, m_indx_info);
  ADDFIELD_EX(kTypeString, INDX_PRICE, m_symbol, pos);
  ADDFIELD_EX(kTypeString, INDX_PRICE, m_price, pos);
  ADDFIELD_EX(kTypeString, INDX_PRICE, m_clean_price, pos);
  ADDFIELD_EX(kTypeString, INDX_PRICE, m_company_id, pos);
  ADDFIELD_EX(kTypeTime, INDX_PRICE, m_modify_time, pos);

  ADDFIELD(kTypeChar, REPORT, m_price_sel);
  ADDFIELD(kTypeString, REPORT, m_tradePrice);
  ADDFIELD(kTypeInt64, REPORT, m_pkg_id);
  BASSERT(index == (int)kReportField::PkgId);
}

char* ReportEncoder::Encode(const char* data, int size, int metaId,
                            int& length) {
  FieldArray* fa = FieldArray::Create(data, GetOffsets());
  length = fa->GetBytes();
  return (char*)fa;
}

int ReportEncoder::Decode(const void* encodedData, int encodedSize,
                          char* rawData, int capcity) {
  if (rawData && capcity >= sizeof(REPORT) &&
      FieldArray::Decode((const FieldArray*)encodedData, rawData, GetOffsets()))
    return sizeof(REPORT);
  return -1;
}
